/* 源数据 */
const data = { foo: 1, bar: 2 };

/* 存储被读取的Proxy代理对象的原始对象的属性的副作用函数集合 */
const bucket = new WeakMap();

/* 代理对象被读的属性的副作用函数 */
let activeEffect;

/* 副作用函数栈 */
const effectStack = [];

export const obj = new Proxy(data, {
  get(target, key) {
    // console.log("读取了obj的属性", key);

    track(target, key);

    return target[key];
  },
  set(target, key, value) {
    target[key] = value;
    // console.log("设置了obj的属性");
    trigger(target, key);

    return true;
  },
});

/**
 * @description 注册副作用函数
 * @param {Function} fn 副作用函数
 */
export function effect(fn, options) {
  const effectFn = () => {
    cleanup(effectFn);

    activeEffect = effectFn;

    /* 在副作用函数执行前将副作用函数压入栈中 */
    effectStack.push(effectFn);

    const res = fn();

    /* 在副作用函数执行完后将副作用函数从副作用函数栈中弹出 */
    effectStack.pop();
    /* 将当前副作用函数恢复到之前的副作用函数 */
    activeEffect = effectStack[effectStack.length - 1];

    return res;
  };

  effectFn.deps = [];

  effectFn.options = options;

  /* 只有在不是懒执行的时候才立即执行 */
  if (!options?.lazy) {
    effectFn();
  }

  return effectFn;
}

/**
 * @description 代理对象被读的属性的副作用函数集合
 * @param {Object} target 代理对象的源对象
 * @param {String} key 被读取的代理的源对象中的属性
 */
export function track(target, key) {
  // console.log("track执行");
  if (!activeEffect) return;

  let depsMap = bucket.get(target);

  if (!depsMap) {
    depsMap = new Map();
    bucket.set(target, depsMap);
  }

  let deps = depsMap[key];

  if (!deps) {
    deps = new Set();
    depsMap.set(key, deps);
  }

  deps.add(activeEffect);
  activeEffect.deps.push(deps);
  // console.log(activeEffect.deps);
}

/**
 * @description 触发副作用函数
 * @param {Object} target 代理对象的源对象
 * @param {String} key 被设置的代理的源对象中的属性
 * @returns
 */
export function trigger(target, key) {
  const depsMap = bucket.get(target);

  if (!depsMap) return;

  const effects = depsMap.get(key);

  /* 防止forEach遍历Set时无限循环（因为副作用函数删了之后又在读取的时候被添加了） */
  const effectsToRun = new Set();

  /* 避免当一个副作用函数中同时出现读取和设置响应式数据无限递归执行副作用函数，导致栈溢出 */
  effects.forEach((effectFn) => {
    if (effectFn !== activeEffect) {
      effectsToRun.add(effectFn);
    }
  });

  /* 执行当前被设置的属性的所有副作用函数 */
  effectsToRun.forEach((effectFn) => {
    if (effectFn.options?.scheduler) {
      /* 调度执行：由用户决定在什么时候以什么方式执行 */
      effectFn.options.scheduler(effectFn);
    } else {
      effectFn();
    }
  });
}

/**
 * @description 清理副作用函数
 * @param {Function} effectFn 副作用函数
 */
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];

    deps.delete(effectFn);
  }

  effectFn.deps.length = 0;
}
